package cn.net.xyan.blossom.security.service

import cn.net.xyan.blossom.dto.security.Permission
import cn.net.xyan.blossom.dto.security.Role
import cn.net.xyan.blossom.dto.security.User
import cn.net.xyan.blossom.kotlin.randomString
import cn.net.xyan.blossom.security.dao.SecurityPermissionDao
import cn.net.xyan.blossom.security.dao.SecurityRoleDao
import cn.net.xyan.blossom.security.dao.SecurityUserInfoDao
import cn.net.xyan.blossom.security.entity.SecurityPermission
import cn.net.xyan.blossom.security.entity.SecurityRole
import cn.net.xyan.blossom.security.entity.SecurityUserInfo
import cn.net.xyan.blossom.security.service.AuthService.Companion.PermissionAdmin
import cn.net.xyan.blossom.security.service.AuthService.Companion.RoleAdmin
import cn.net.xyan.blossom.security.service.AuthService.Companion.UserAdmin
import org.springframework.beans.factory.annotation.Autowired
import javax.annotation.PostConstruct
import org.apache.shiro.SecurityUtils

interface AuthService{

    companion object {
        const val PermissionAdmin = "Admin"
        const val RoleAdmin = "Admin"
        const val UserAdmin = "admin"
    }

    fun setupPermission(p: Permission): SecurityPermission
    fun savePermission(p: Permission):SecurityPermission
    fun queryPermission(name:String):SecurityPermission?

    fun setupRole(role: Role): SecurityRole
    fun saveRole(role: Role):SecurityRole
    fun queryRole(name:String):SecurityRole?

    fun setupUser(user: User): SecurityUserInfo
    fun saveUser(user: User):SecurityUserInfo
    fun queryUser(username:String):SecurityUserInfo?

    fun checkPermission(user:SecurityUserInfo,p: SecurityPermission):Boolean

    fun currentUser():SecurityUserInfo?

}

class DefaultAuthService:AuthService{

    @Autowired
    lateinit var userDao: SecurityUserInfoDao

    @Autowired
    lateinit var roleDao: SecurityRoleDao

    @Autowired
    lateinit var permissionDao: SecurityPermissionDao

    @PostConstruct
    fun setup(){
        val p = Permission(PermissionAdmin,PermissionAdmin)
        val r = Role(RoleAdmin, listOf(p))
        val u = User(UserAdmin, UserAdmin, listOf(r))

        setupUser(u)
    }

    override fun setupPermission(p: Permission): SecurityPermission {
        return queryPermission(p.name)?:savePermission(p)
    }

    override fun savePermission(p: Permission): SecurityPermission {
        var permission = SecurityPermission(p.name)
        permission.permission = p.permission
        permission.available = true

        return permissionDao.saveAndFlush(permission)
    }

    override fun queryPermission(name: String): SecurityPermission? {
        return permissionDao.findByName(name)
    }

    override fun setupRole(role: Role): SecurityRole {
        return queryRole(role.role)?:saveRole(role)
    }

    override fun saveRole(role: Role): SecurityRole {
        var r = SecurityRole(role.role)

        r.description = role.description
        r.available = true
        role.permissions.forEach {
            val p = setupPermission(it)
            r.permissions.add(p)
        }

        return roleDao.saveAndFlush(r)
    }

    override fun queryRole(name: String): SecurityRole? {
        return roleDao.findByRole(name)
    }

    override fun setupUser(user: User): SecurityUserInfo {
        return queryUser(user.username)?:saveUser(user)
    }

    override fun saveUser(user: User): SecurityUserInfo {
        var u = SecurityUserInfo(user.username)
        u.password = user.password
        u.salt = randomString(8)
        u.realname = user.realName

        user.roles.forEach {
            val role = setupRole(it)
            u.roles.add(role)
        }

        return userDao.saveAndFlush(u)
    }

    override fun queryUser(username: String): SecurityUserInfo? {
        return userDao.findByUsername(username)
    }

    override fun checkPermission(user: SecurityUserInfo, p: SecurityPermission): Boolean {
        return permissionDao.checkPermission(user, p).isNotEmpty()

    }

    override fun currentUser(): SecurityUserInfo? {
        val currentUser = SecurityUtils.getSubject()

        if (currentUser.isAuthenticated){
            if (currentUser.principal is SecurityUserInfo){
                val username = currentUser?.principals?.oneByType(SecurityUserInfo::class.java)?.username
                return queryUser(username?:"")
            }
        }
        return null
    }

}